using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;

namespace BookBarter
{
    public class Person
    {
        private static List<Person> persons = new List<Person>();

        //class variables
        private String emailAddress;
        private String firstName;
        private String lastName;
        private String userId;
        private List<BookHave> bookHaves;
        private List<BookWant> bookWants;
        private Address address1;
        private Address address2;
    
        //class properties
        public String EmailAddress
        {
            get{
                return emailAddress;
            }
            set
            {
                emailAddress = value;
                Save();
            }
        }
        public String FirstName
        {
            get
            {
                return firstName;
            }
            set
            {
                firstName = value;
                Save();
            }
        }
        public String LastName
        {
            get
            {
                return lastName;
            }
            set
            {
                lastName = value;
                Save();
            }
        }
        public String UserId
        {
            get
            {
                return userId;
            }
            private set
            {
                userId = value;
            }
        }
        public List<BookBarter.BookWant> BookWants
        {
            get
            {
                return new List<BookWant>(bookWants);
            }
            private set
            {
                bookWants = value;
            }
        }
        public List<BookBarter.BookHave> BookHaves
        {
            get
            {
                return new List<BookHave>(bookHaves);
            }
            private set
            {
                bookHaves = value;
            }
        }
        public Address Address1
        {
            get
            {
                return address1;
            }
            set
            {
                address1 = value;
                Save();
            }
        }
        public Address Address2
        {
            get
            {
                return address2;
            }
            set
            {
                address2 = value;
                Save();
            }
        }

        //constructor
        private Person(String fname, String lname, String ID, String email, Address address1, Address address2)
        {
            bookHaves = new List<BookHave>();
            bookWants = new List<BookWant>();
            this.FirstName = fname;
            this.LastName = lname;
            this.UserId = ID;
            this.EmailAddress = email;
            this.Address1 = address1;
            this.Address2 = address2;
        }

        /// <summary>
        /// Adds a new book to the person's list of wants
        /// </summary>
        /// <param name="newWant">book the person wants</param>
        public void addBookWant(BookWant newWant)
        {
            bookWants.Add(newWant);
        }

        /// <summary>
        /// removes a book that the person wants
        /// </summary>
        /// <param name="remove">the book to be removed</param>
        public void removeBookWant(BookWant remove)
        {
            this.BookWants.Remove(remove);
            this.Save();
        }

        /// <summary>
        /// Adds a new book to the person's list of books they have
        /// </summary>
        /// <param name="newHave">book the person has</param>
        public void addBookHave(BookHave newHave)
        {
            bookHaves.Add(newHave);
        }

        /// <summary>
        /// removes a book that the person has
        /// </summary>
        /// <param name="remove">the book to be removed</param>
        public void removeBookHave(BookHave remove)
        {
            this.BookHaves.Remove(remove);
            this.Save();
        }

        /// <summary>
        /// Checks to see if person has a specific book
        /// </summary>
        /// <param name="book">Book to check for</param>
        /// <returns>True if they have the book, false if they do not</returns>
        public bool checkBookHave(Book book)
        {
            foreach (BookHave have in bookHaves)
                if (have.Book == book)
                { return true; }

            return false; //returns false if book is not found in list of haves
        }

        /// <summary>
        /// Checks to see if person wants a specific book
        /// </summary>
        /// <param name="book">Book to check for</param>
        /// <returns>True if they want the book, false if they do not</returns>
        public bool checkBookWant(Book book)
        {
            foreach (BookWant want in BookWants)
                if (want.Book == book)
                { return true; }

            return false; //returns false if book is not found in list of wants
        }

        /* to be finished */
		/// <summary>
		/// Get person by userid
		/// </summary>
		/// <param name="userid">The ID of the user we are trying to get</param>
		/// <returns>returns Person object for the user with the given ID</returns>
        public static Person GetPerson(String userid)
        {
            foreach (Person p in persons)
                if (p.UserId.Equals(userid))
                    return p;
            //create defaults for variables
            String firstName = "", lastName = "", email = "";
            Guid address1, address2;
            //grab connection to dbms
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            //create the query
            SqlCommand query = new SqlCommand("EXEC get_person @userid", cnx);
            query.Parameters.Add("@userid", System.Data.SqlDbType.VarChar);
            query.Parameters["@userid"].SqlValue = userid;

            //execute the query
            SqlDataReader result = query.ExecuteReader();
            try
            {
                //set info
                if (result.Read())
                {
                    firstName = result["first_name"].ToString();
                    lastName = result["last_name"].ToString();
                    email = result["email"].ToString();
                    address1 = result.GetGuid(result.GetOrdinal("address1"));
                    address2 = result.GetGuid(result.GetOrdinal("address2"));
                }
                //else - the book is INVALID
                else
                {
                    throw new NoSuchPerson("The person does not exist!");
                }
            }
            finally
            {
                result.Close();
            }

            //if we are here... return the book hold object
            Person requestedPerson = new Person(firstName, lastName, userid, email, Address.GetAddress(address1), Address.GetAddress(address2));
            requestedPerson.loadBookWants();
            requestedPerson.loadBookHaves();
            persons.Add(requestedPerson);
            return requestedPerson;
        }
        /* to be finished */
		/// <summary>
		/// Creates a new person
		/// </summary>
		/// <param name="firstName">The first name of the new person</param>
		/// <param name="lastName">The last name of the new person</param>
		/// <param name="userID">The user id for the new person</param>
		/// <param name="email">The email address of the new person</param>
		/// <returns>A Person object for the person we just created</returns>
        public static Person CreatePerson(String firstName, String lastName, String userID, String email)
        {
            Person newPerson = null;
            Address address1 =  Address.CreateAddress("", "", ""), address2 = Address.CreateAddress("", "", "");
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC add_person @userid, @first_name, @last_name, @email, @address1, @address2", cnx);

            query.Parameters.Add("@first_name", System.Data.SqlDbType.VarChar);
            query.Parameters["@first_name"].SqlValue = firstName;

            query.Parameters.Add("@last_name", System.Data.SqlDbType.VarChar);
            query.Parameters["@last_name"].SqlValue = lastName;

            query.Parameters.Add("@userid", System.Data.SqlDbType.VarChar);
            query.Parameters["@userid"].SqlValue = userID;

            query.Parameters.Add("@email", System.Data.SqlDbType.VarChar);
            query.Parameters["@email"].SqlValue = email;

            query.Parameters.Add("@address1", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@address1"].SqlValue = address1.ID;

            query.Parameters.Add("@address2", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@address2"].SqlValue = address2.ID;

            SqlDataReader result = query.ExecuteReader();
            try
            {
                if (result.HasRows)
                {
                    if (result.Read())
                    {
                        if (result.IsDBNull(result.GetOrdinal("userid")))
                        {
                            result.Close();
                            throw new MalformedPersonRecord("NULL where data is expected for record ID.");
                        }
                        newPerson = Person.GetPerson(result.GetString(result.GetOrdinal("userid")));
                    }
                    else
                        throw new NewPersonException("No person exists for the new person id.");
                }
            }
            finally
            {
                result.Close();
            }
            return newPerson;
        }
		/* to be finished */
		/// <summary>
		/// Saves changes to any person
		/// </summary>
        private void Save()
        {
            //grab connection to dbms
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC update_person @userid, @first_name, @last_name, @email, @address1, @address2", cnx);
            query.Parameters.Add("@first_name", System.Data.SqlDbType.VarChar);
            query.Parameters["@first_name"].SqlValue = this.FirstName;

            query.Parameters.Add("@last_name", System.Data.SqlDbType.VarChar);
            query.Parameters["@last_name"].SqlValue = this.LastName;

            query.Parameters.Add("@userid", System.Data.SqlDbType.VarChar);
            query.Parameters["@userid"].SqlValue = this.UserId;

            query.Parameters.Add("@email", System.Data.SqlDbType.VarChar);
            query.Parameters["@email"].SqlValue = this.EmailAddress;

            query.Parameters.Add("@address1", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@address1"].SqlValue = this.Address1.ID;

            query.Parameters.Add("@address2", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@address2"].SqlValue = this.Address2.ID;

            query.ExecuteNonQuery();
        }

		/// <summary>
		/// Loads all book haves for the person into local memory
		/// </summary>
        private void loadBookHaves()
        {
            //grab connection to dbms
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            //create the query
            SqlCommand query = new SqlCommand("EXEC get_book_haves @userid", cnx);
            query.Parameters.Add("@userid", System.Data.SqlDbType.VarChar);
            query.Parameters["@userid"].SqlValue = this.UserId;

            //execute the query
            SqlDataReader result = query.ExecuteReader();
            try
            {
                //set info
                if (result.Read())
                {
                    this.addBookHave(BookHave.GetBookHave(result.GetGuid(result.GetOrdinal("haveid"))));
                }
            }
            finally
            {
                result.Close();
            }
        }

		/// <summary>
		/// Loads all book wants for the person into local memory
		/// </summary>
        private void loadBookWants()
        {
            //grab connection to dbms
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            //create the query
            SqlCommand query = new SqlCommand("EXEC get_book_wants @userid", cnx);
            query.Parameters.Add("@userid", System.Data.SqlDbType.VarChar);
            query.Parameters["@userid"].SqlValue = this.UserId;

            //execute the query
            SqlDataReader result = query.ExecuteReader();
            try
            {
                //set info
                if (result.Read())
                {
                    this.addBookWant(BookWant.GetBookWant(result.GetGuid(result.GetOrdinal("wantid"))));
                }
            }
            finally
            {
                result.Close();
            }
		}
		/// <summary>
		/// Returns a List of TradeRoute objects for all participating trade routes
		/// </summary>
		/// <returns>A List of TradeRoute objects for all participating trade routes</returns>
        public List<TradeRoute> GetParticipatingTradeRoutes()
        {
            List<TradeRoute> tradeRoutes = new List<TradeRoute>();
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC get_all_trades_routes @userid", cnx);
            query.Parameters.Add("@userid", System.Data.SqlDbType.VarChar);
            query.Parameters["@userid"].SqlValue = this.UserId;
            SqlDataReader result = query.ExecuteReader();
            try
            {
                while (result.Read())
                {
                    tradeRoutes.Add(TradeRoute.GetTradeRoute(result.GetGuid(result.GetOrdinal("routeid"))));
                }
            }
            finally
            {
                result.Close();
            }
            return tradeRoutes;
		}
		/// <summary>
		/// Returns a Person object for a random person
		/// </summary>
		/// <returns>A Person object for a random person</returns>
        public static Person GetRandomPerson()
        {
            Person randomPerson;
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC get_person_count", cnx);
            SqlDataReader result = query.ExecuteReader();
            try
            {
                result.Read();
                int totalUsers = result.GetInt32(0);
                result.Close();
                query = new SqlCommand("EXEC get_all_persons", cnx);
                result = query.ExecuteReader();
                int personPosition = (new Random()).Next(totalUsers + 1);
                for (int i = 0; i < personPosition; i++)
                    result.Read();
                return Person.GetPerson(result["userid"].ToString());
            }
            finally
            {
                result.Close();
            }
        }
    }
}
